home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Sample Controls / ListBox / CListBoxControl.cpp next >
Encoding:
Text File  |  1996-12-20  |  13.1 KB  |  555 lines  |  [TEXT/????]

  1. // =================================================================================
  2. //
  3. //    CListBoxControl.cpp                ©1996 Microsoft Corporation All rights reserved.
  4. //
  5. // =================================================================================
  6.  
  7. #include "ocheaders.h"
  8. #include "CListBoxControl.h"
  9.  
  10.  
  11. /************* Local ListBox defines ****************/
  12. #define SCROLL_BAR_WIDTH     15
  13. #define    KEY_UPARROW            0x1E
  14. #define    KEY_DOWNARROW        0x1F
  15.  
  16. const Uint32    DataAvailableIdleRefCon    = 1;
  17. const Int32        DataAvailableIdleTime    = 30;    //    half a second
  18.  
  19.  
  20. #pragma mark === CListBoxControl::Construction & Destruction ===
  21.  
  22. //
  23. //  CListBoxControl::CListBoxControl
  24. //
  25.  
  26. CListBoxControl::CListBoxControl(void)
  27. {
  28.     mListH = NULL;
  29.     mData = NULL;
  30.     mLoaded = false;
  31.     mItemCount = 0;
  32.     mItemInProgressIndex = 0;
  33.     mSelectedCell.h = -1;
  34.     mSelectedCell.v = -1;
  35. }
  36.  
  37. //
  38. //  CListBoxControl::~CListBoxControl
  39. //
  40.  
  41. CListBoxControl::~CListBoxControl()
  42. {
  43.     if (mListH)
  44.         ::LDispose(mListH);
  45.     if (mData)
  46.         ::DisposeHandle( Handle(mData) );
  47. }
  48.  
  49.  
  50. #pragma mark === CListBoxControl::IUnknown ===
  51.  
  52. //
  53. //  CListBoxControl::IUnknown::QueryInterface
  54. //
  55. //  Returns a pointer to the specified interface on a component to which a
  56. //  client currently holds an interface pointer.
  57. //
  58. STDMETHODIMP
  59. CListBoxControl::QueryInterface(REFIID inRefID, void** outObj)
  60. {
  61.     if ( inRefID == IID_IBindStatusCallback )
  62.         return CBaseBindStatusCallback::QueryInterface(inRefID, outObj);
  63.     else
  64.         return CBaseControl::QueryInterface(inRefID, outObj);
  65. }
  66.  
  67.  
  68. #pragma mark === CListBoxControl::IControl ===
  69.  
  70. //
  71. //  CListBoxControl::IControl::Draw
  72. //
  73.  
  74. STDMETHODIMP
  75. CListBoxControl::Draw(DrawContext* inContext)
  76. {
  77.     Rect        ListRect;
  78.     
  79.     if (inContext->DrawAspect != DVASPECT_CONTENT)
  80.         return DV_E_DVASPECT;
  81.  
  82.     // Set the font to what we want
  83.     TextFont(1);
  84.     TextFace(0);
  85.     TextMode(srcCopy);
  86.     TextSize(12);
  87.     
  88.     // if we have the focus, show the user
  89.     if ( mOwnedFoci & KeyboardFocus )
  90.     {
  91.         ::PenSize(2, 2);
  92.         ::FrameRect(&inContext->Location);
  93.         ::PenSize(1, 1);
  94.     }
  95.     
  96.     // Now frame the list
  97.     ::SetRect(&ListRect, inContext->Location.left + 4, inContext->Location.top + 4, inContext->Location.right - 4 - SCROLL_BAR_WIDTH, inContext->Location.bottom - 4);        
  98.     ::FrameRect(&ListRect);
  99.     
  100.     //    if we don't have a list handle yet, then create it
  101.     if (!mListH)
  102.     {
  103.         Point    CellSize;
  104.         Rect    DataBounds;
  105.  
  106.         ::SetRect(&DataBounds, 0, 0, 1, 0);
  107.         ::SetRect(&ListRect, inContext->Location.left + 5, inContext->Location.top + 5, inContext->Location.right - 5 - SCROLL_BAR_WIDTH, inContext->Location.bottom - 5);        
  108.         SetPt( &CellSize, ListRect.right - ListRect.left, 14);
  109.         mListH = ::LNew(&ListRect, &DataBounds, CellSize, 0, inContext->Port, true, false, false, true);
  110.     }
  111.  
  112.     // if there is a list handle, populate it if necessary and draw it
  113.     if (mListH)
  114.     {
  115.         listitemdata    *ListItem;
  116.         Int16            i;
  117.         
  118.         // Populate the list manager list from our data for items that haven't been inserted
  119.         HLock( Handle( mData ));
  120.         ::LDoDraw(false, mListH);
  121.         for (i = 0, ListItem = *mData; i < mItemCount; i++, ListItem++)
  122.         {
  123.             if (!ListItem->Inserted)
  124.             {
  125.                 Cell    NewCell;
  126.                 NewCell.h = 0;
  127.                 NewCell.v = i;
  128.                 ::LAddRow(1, i, mListH);
  129.                 ::LSetCell(&ListItem->Data[1], ListItem->Data[0], NewCell, mListH);
  130.                 ListItem->Inserted = true;
  131.             }
  132.         }
  133.         ::LDoDraw(true, mListH);
  134.         HUnlock( Handle( mData ));
  135.  
  136.         // Draw the list
  137.         LUpdate(((WindowPtr) inContext->Port)->visRgn, mListH);
  138.  
  139.         // if we don't have the focus, make sure the scroll bars are white
  140.         if ( !(mOwnedFoci & KeyboardFocus) )
  141.         {
  142.             Rect ScrollRect;
  143.             
  144.             SetRect(&ScrollRect, inContext->Location.right - 5 - SCROLL_BAR_WIDTH, inContext->Location.top + 4, inContext->Location.right - 4, inContext->Location.bottom-4);
  145.             EraseRect(&ScrollRect);
  146.             FrameRect(&ScrollRect);
  147.         }
  148.         
  149.         mChanged = false;
  150.     }
  151.  
  152.     return S_OK;
  153. }
  154.  
  155.  
  156. //
  157. //  CListBoxControl::IControl::DoMouse
  158. //
  159.  
  160. STDMETHODIMP
  161. CListBoxControl::DoMouse(MouseEventType inMouseET, PlatformEvent* inEvent)
  162. {
  163.     DrawContext        Context = {BeginPortType};
  164.     PlatformPoint    localPt;
  165.     Point             Cell = {0, 0};
  166.     Boolean8        acquired = false;
  167.  
  168.     if (inMouseET == MouseDown)
  169.     {
  170.         if (!(mOwnedFoci & KeyboardFocus))
  171.         {
  172.             if (mContainerSiteP->RequestFocus(true, KeyboardFocus) == S_OK)
  173.             {
  174.                 mOwnedFoci = FocusSet(mOwnedFoci | KeyboardFocus);
  175.                 acquired = true;
  176.             }
  177.         }
  178.         
  179.         mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &Context);
  180.  
  181.         TextFont(1);
  182.         TextFace(0);
  183.         TextMode(srcCopy);
  184.         TextSize(12);
  185.  
  186.         localPt = inEvent->where;
  187.         ::GlobalToLocal(&localPt);
  188.         
  189.         if (::LClick(localPt, 0, mListH))
  190.             ::SysBeep(1);
  191.  
  192.         // If we've acquired the keyboard focus and we have a saved selection,
  193.         // do we need to restore it?            
  194.         if (acquired &&    
  195.             (mSelectedCell.h != -1 && mSelectedCell.v != -1))
  196.         {
  197.             // If the click didn't select an item in the list, restore the saved selection
  198.             if (!(::LGetSelect(true, &Cell, mListH)))
  199.                 ::LSetSelect(true, mSelectedCell, mListH);        // select cell
  200.             mSelectedCell.h = -1;
  201.             mSelectedCell.v = -1;
  202.         }
  203.  
  204.         // If we don't have the focus, we have a saved selection, and the click
  205.         // selected an item in the list, update the saved selection (used when
  206.         // we receive the expected SetFocus call).
  207.         if (!(mOwnedFoci & KeyboardFocus) && 
  208.             (mSelectedCell.h != -1 && mSelectedCell.v != -1) &&
  209.             ::LGetSelect(true, &Cell, mListH))
  210.         {
  211.             mSelectedCell = Cell;
  212.         }
  213.  
  214.         mContainerSiteP->ReleaseContext(&Context);
  215.     }
  216.  
  217.     return S_OK;
  218. }
  219.  
  220.  
  221. //
  222. //  CListBoxControl::IControl::DoKey
  223. //
  224.  
  225. STDMETHODIMP
  226. CListBoxControl::DoKey(KeyEventType inKeyET, Char8 inChar, PlatformEvent* inEvent)
  227. {
  228. #pragma unused (inEvent)
  229.     DrawContext        Context = {BeginPortType};
  230.  
  231.     mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &Context);
  232.  
  233.     TextFont(1);
  234.     TextFace(0);
  235.     TextMode(srcCopy);
  236.     TextSize(12);
  237.  
  238.     if ((inKeyET == KeyDown) || (inKeyET == AutoKey))
  239.     {
  240.         // if we have the focus, handle the key down
  241.         if ( mOwnedFoci & KeyboardFocus )
  242.         {
  243.             Boolean    BeyondBounds = false;
  244.             Boolean    UnSelectCell = true;
  245.             Point    InCell = {0, 0};
  246.             Point    OutCell = {0, 0};
  247.             
  248.             // We only care about up and down arrow keypresses
  249.             if ( inChar == KEY_UPARROW || inChar == KEY_DOWNARROW )
  250.             {
  251.                 // if we find a selection, move it in the appropriate direction
  252.                 if ( ::LGetSelect(true, &InCell, mListH) )
  253.                 {
  254.                     OutCell = InCell;
  255.                     // Turn on the selection in the up or down cell
  256.                     switch ( inChar )
  257.                     {
  258.                         case KEY_UPARROW:
  259.                             if ( OutCell.v > 0 )
  260.                                 OutCell.v --;    
  261.                             else
  262.                                 BeyondBounds = true;
  263.                             break;
  264.                             
  265.                         case KEY_DOWNARROW:
  266.                             if ( OutCell.v < (mItemCount-1) )
  267.                                 OutCell.v ++;
  268.                             else
  269.                                 BeyondBounds = true;
  270.                             break;
  271.                     }
  272.                 }
  273.                 //    no selection means that we go by the arrows for the first one
  274.                 else
  275.                 {
  276.                     UnSelectCell = false;
  277.                     switch ( inChar )
  278.                     {
  279.                         case KEY_UPARROW:
  280.                             OutCell.v = 0;
  281.                             break;
  282.                         case KEY_DOWNARROW:
  283.                             OutCell.v = mItemCount - 1;
  284.                             break;
  285.                     }
  286.                 }
  287.  
  288.                 //    handle exit chores of changing selection
  289.                 if (!BeyondBounds && UnSelectCell)
  290.                     ::LSetSelect(false, InCell, mListH);
  291.                 if (!BeyondBounds)
  292.                 {
  293.                     ::LSetSelect(true, OutCell, mListH);
  294.                     ::LAutoScroll(mListH);
  295.                 }
  296.                 else
  297.                     ::SysBeep(1);
  298.             }
  299.         }
  300.     }
  301.             
  302.     mContainerSiteP->ReleaseContext(&Context);
  303.     
  304.     return S_OK;
  305. }
  306.  
  307.  
  308. //
  309. //  CListBoxControl::IControl::DoIdle
  310. //
  311.  
  312. STDMETHODIMP
  313. CListBoxControl::DoIdle(Uint32 inIdleRefCon)
  314. {
  315. #pragma unused (inIdleRefCon)
  316.     if (inIdleRefCon == DataAvailableIdleRefCon)
  317.     {
  318.         DrawContext        Context = {BeginPortType};
  319.  
  320.         if (mChanged)
  321.         {
  322.             if (mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &Context) == S_OK)
  323.             {
  324.                 InvalRect(&Context.Location);
  325.                 mContainerSiteP->ReleaseContext(&Context);
  326.             }
  327.         }
  328.  
  329.         if (mLoaded)
  330.             mContainerSiteP->SetIdleTime(RemoveIdler, DataAvailableIdleRefCon);
  331.     }
  332.  
  333.     return S_OK;
  334. }
  335.  
  336.  
  337. //=--------------------------------------------------------------------------=
  338. //  CBaseControl:IControl:SetFocus    
  339. //
  340. //  Since we handle focus, we override the base class method for our needs
  341. //
  342. //=--------------------------------------------------------------------------=
  343. STDMETHODIMP
  344. CListBoxControl::SetFocus(FocusCommand inCommand, FocusSet inFocus)
  345. {
  346.     DrawContext Context = {BeginPortType};
  347.     ErrorCode    ReturnValue = S_OK;
  348.     FocusSet    InOwnedFoci = mOwnedFoci;
  349.  
  350.     //    a TakeNext or TakePrev if we don't have the focus, means take it
  351.     if ((inCommand == TakeNextCommand || inCommand == TakePrevCommand) && !mOwnedFoci)
  352.     {
  353.         //     if the container is offering us the one focus we want, take all of them
  354.         if (inFocus & KeyboardFocus)
  355.             mOwnedFoci = inFocus;
  356.         else
  357.         //    otherwise, say no thanks
  358.             ReturnValue = E_FAIL;
  359.     }
  360.     //    a TakeNext or a TakePrev on a control which doesn't embed and has the focus should fail
  361.     else if (inCommand == TakeNextCommand || inCommand == TakePrevCommand)
  362.     {
  363.         ReturnValue = E_FAIL;
  364.     }
  365.     //    we're being asked/told to release our foci - always comply
  366.     else // if (Spec == ReleaseRequest || Spec == ReleaseCommand)
  367.     {
  368.         if (inFocus & mOwnedFoci != inFocus)
  369.             DebugStr("\pWhat are they doing asking to release foci we don't have?");
  370.  
  371.         mOwnedFoci = FocusSet(mOwnedFoci & ~inFocus);    //    really easy for us
  372.     }
  373.  
  374.     mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &Context);
  375.  
  376.     if (!(InOwnedFoci & KeyboardFocus) && (mOwnedFoci & KeyboardFocus))
  377.     {
  378.         // Acquired keyboard focus
  379.         if(mSelectedCell.h != -1 && mSelectedCell.v != -1)
  380.         {
  381.             ::LSetSelect(true, mSelectedCell, mListH);        // select cell
  382.             mSelectedCell.h = -1;
  383.             mSelectedCell.v = -1;
  384.         }
  385.     }
  386.     else if ((InOwnedFoci & KeyboardFocus) && !(mOwnedFoci & KeyboardFocus))
  387.     {
  388.         // Lost keyboard focus
  389.         Point Cell = {0, 0};
  390.  
  391.         if ( ::LGetSelect(true, &Cell, mListH) )
  392.         {
  393.             mSelectedCell = Cell;
  394.             ::LSetSelect(false, Cell, mListH);        // deselect cell
  395.         }
  396.     }
  397.  
  398.     mContainerSiteP->ReleaseContext(&Context);
  399.     
  400.     // Invalidate the position rectangle so we'll get redrawn
  401.     //    if the focus state of the keyboard changed
  402.     if ((InOwnedFoci & KeyboardFocus) != (mOwnedFoci & KeyboardFocus))
  403.     {
  404.         InvalAllContexts();
  405.     }
  406.     
  407.     return ReturnValue;
  408. }
  409.  
  410.  
  411. #pragma mark === CListBoxControl::IPersistPropertyBag ===
  412.  
  413. //
  414. //  CListBoxControl::IPersistPropertyBag::Load
  415. //
  416.  
  417. STDMETHODIMP
  418. CListBoxControl::Load(IPropertyBag* PropertyBag, IErrorLog* pErrorLog)
  419. {
  420.     ErrorCode            ErrCode = E_FAIL;
  421.  
  422.     LoadTextState(PropertyBag, pErrorLog);
  423.  
  424.     ErrCode = OpenStream(mContainerSiteP, (char*)mDataURL, FALSE);
  425.     
  426.     return ErrCode;
  427. }
  428.  
  429.  
  430. //=--------------------------------------------------------------------------=
  431. // CListBoxControl::LoadTextState
  432. //=--------------------------------------------------------------------------=
  433. // load in our text state for this control.
  434. //
  435. // Parameters:
  436. //    IPropertyBag *        - [in] property bag to read from
  437. //    IErrorLog *           - [in] errorlog object to use with proeprty bag
  438. //
  439. // Output:
  440. //    ErrorCode
  441. //
  442. STDMETHODIMP CListBoxControl::LoadTextState(IPropertyBag *PropertyBag, IErrorLog *pErrorLog)
  443. {
  444.     VARIANT v;
  445.     Uint32    length;
  446.  
  447. //    VariantInit(&v);
  448.  
  449.     v.vt = VT_BSTR;
  450.     v.bstrVal = NULL;
  451.  
  452.     // try to load in the property.  if we can't get it, then leave
  453.     // things at their default.
  454.     //
  455.     PropertyBag->Read("data", &v, pErrorLog);
  456.     if (v.bstrVal)
  457.     {
  458.         length = *((Uint32*) v.bstrVal) ;
  459.         strcpy((Char8*)mDataURL, v.bstrVal + sizeof(Uint32));
  460.         CoTaskMemFree(v.bstrVal);
  461. //        VariantInit(&v);
  462.     }
  463.  
  464.     return S_OK;
  465. }
  466.  
  467.  
  468. #pragma mark === CListBoxControl::IBindStatusCallback ===
  469.  
  470. //
  471. //  CListBoxControl::OnStopBinding
  472. //
  473.  
  474.  
  475. STDMETHODIMP
  476. CListBoxControl::OnStopBinding(ErrorCode Result, const Char8* Error)
  477. {
  478.     CBaseBindStatusCallback::OnStopBinding(Result, Error);
  479.     
  480.     mLoaded = true;
  481.  
  482.     if (mBindSiteP)
  483.     {
  484.         mBindSiteP->Release();
  485.         mBindSiteP = 0;
  486.     }
  487.  
  488.     return S_OK;
  489. }
  490.  
  491.  
  492. //
  493. //  CListBoxControl::OnDataAvailable
  494. //
  495.  
  496. STDMETHODIMP
  497. CListBoxControl::OnDataAvailable(Uint32 BSCF, Uint32 Size, FORMATETC* FormatEtc, STGMEDIUM* StgMedium)
  498. {
  499.     OSErr Err = noErr;
  500.     
  501.     CBaseBindStatusCallback::OnDataAvailable(BSCF, Size, FormatEtc, StgMedium);
  502.     
  503.     if (StgMedium->tymed == TYMED_ISTREAM)
  504.     {
  505.         if (!mData && Size)
  506.         {
  507.             mData = (listitemdata**)(::NewHandle(0));
  508.             mContainerSiteP->SetIdleTime(DataAvailableIdleTime, DataAvailableIdleRefCon);
  509.         }
  510.  
  511.         if (mData)
  512.         {
  513.             Boolean    EndOfFile = Size == 0;
  514.  
  515.             while (EndOfFile || Size--)    //    order is important here - if EOF is true then we don't want to change dwSize!
  516.             {
  517.                 Char8    InChar;
  518.  
  519.                 if (EndOfFile)
  520.                 {
  521.                     InChar = '\r';
  522.                     EndOfFile = false;
  523.                 }
  524.                 else
  525.                     StgMedium->pstm->Read(&InChar, 1, NULL);
  526.  
  527.                 //    if the charater isn't a newline then add it into the buffer
  528.                 if (InChar != '\r' && InChar != '\n')
  529.                 {
  530.                     if (mItemInProgressIndex < 254)
  531.                         mItemInProgress.Data[++mItemInProgressIndex] = InChar;
  532.                 }
  533.                 else if (mItemInProgressIndex > 0)
  534.                 //    terminate the item in the buffer, add it to the handle
  535.                 {
  536.                     mItemInProgress.Data[0] = mItemInProgressIndex;
  537.                     mItemInProgress.Inserted = false;
  538.                     mChanged = true;
  539.                     ::SetHandleSize( Handle(mData), sizeof(listitemdata) * (mItemCount + 1));
  540.                     ::BlockMove( (Char8*)(&mItemInProgress), (Char8*)((*mData) + mItemCount++), sizeof(listitemdata));
  541.  
  542.                     //    start putting data at the beginning again
  543.                     mItemInProgressIndex = 0;
  544.                 }
  545.             }
  546.         }
  547.     }
  548.     if (StgMedium->pUnkForRelease != NULL)
  549.         StgMedium->pUnkForRelease->Release();
  550.  
  551.     return S_OK;
  552. }
  553.  
  554.  
  555.